﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Magnet.Engine.Entities
{
    public enum MagnetType
    {
        Conical,    //A magnet that applies a conical force.
        Polar,      //A magnet that applies two opposite conical forces.
        Circular   //A magnet that applies a conical force that is 360 degrees.
    }

    public class Magnet : Entity
    {
        //The type of Magnet we have. See the enum above.
        MagnetType type;

        //The amount that we pull things. Can be positive or negative.
        public float attraction;

        //Theta is not a rotation value, it is the size of the angle that the Magnet
        //applies attraction. The angle for the cone in a Conical magnet, and the
        //angle of each of the two cones for Polar. A value of over 180 is not allowed for this.
        float theta;

        //Whether or not you can click this magnet in order to toggle its force.
        bool canBeToggled;

        public Magnet(Vector2 position, MagnetType typ, float attract, float rot, float thet, bool canToggle)
            : base(position, new Vector2(50,50), null)
        {
            type = typ;
            attraction = -attract;
            rotation = rot;
            theta = thet;
            canBeToggled = canToggle;

            if (type == MagnetType.Conical)
            {
                texture = TextureManager.LoadTexture("HorseShoeMagnetN");
                size.X = 64;
                size.Y = 64;
            }
            else if (type == MagnetType.Circular)
            {
                texture = TextureManager.LoadTexture("ToggleMagnetN");
                size.X = 64;
                size.Y = 64;
            }
            else if (type == MagnetType.Polar)
            {
                texture = TextureManager.LoadTexture("PolarMagnet");
                size.X = 64;
                size.Y = 96;
            }
        }

        //Because a magnet does not move, we should override Entity's moving from velocity.
        public override void Update(float delta)
        {
            if (type == MagnetType.Conical)
            {
                if (attraction >= 0)
                    texture = TextureManager.LoadTexture("HorseShoeMagnetN");
                else
                    texture = TextureManager.LoadTexture("HorseShoeMagnetS");
            }
            else if (type == MagnetType.Circular)
            {
                if (attraction >= 0)
                    texture = TextureManager.LoadTexture("ToggleMagnetN");
                else
                    texture = TextureManager.LoadTexture("ToggleMagnetS");
            }

            for (int i = 0; i < World.GetEntityCount(); i++)
            {
                Entity e = World.GetEntityAt(i);
                if (e != this)
                {
                    Vector2 vel = GetForceAtPosition(e.Position);
                    vel.X *= delta / World.NORMAL_TIME_CHANGE;
                    vel.Y *= delta / World.NORMAL_TIME_CHANGE;

                    float sign = Math.Abs(attraction) / attraction;

                    //Now that we know how much force we want to apply,
                    //we may want to apply it differently to different entities.
                    if (e is Debris)
                    {
                        vel *= -sign;
                    }
                    else if (e is Player)
                    {
                        Vector2 playerForward = new Vector2((float)Math.Cos(e.Rotation - Math.PI / 2), (float)Math.Sin(e.Rotation - Math.PI / 2));
                        Vector2 myForward = new Vector2(0,0);

                        if (type == MagnetType.Circular || type == MagnetType.Conical)
                        {
                            myForward = e.Position - position;
                        }
                        else if (type == MagnetType.Polar)
                        {
                            myForward = new Vector2((float)Math.Cos(rotation - Math.PI / 2), (float)Math.Sin(rotation - Math.PI / 2));
                        }
                        float direction = Vector2.Dot(playerForward,myForward);
                        vel *= -(Math.Abs(direction) / direction);
                    }

                    e.ApplyVelocity(vel);
                }
            }

            base.Update(delta);
        }

        /// <summary>
        /// This is what should be used to determine the strength of the magnetic field.
        /// Because magnets are not all single polarity with 360 degrees, however, the
        /// amount of force they apply can depend upon the position. As a result, when
        /// you want to apply force to something with a magnet, pass in the position of the
        /// object you want to affect. The result can be (0,0) if the pos is outside of the cone.
        /// </summary>
        /// <param name="pos">The position to apply force to.</param>
        /// <returns>The force that will be applied at that position.</returns>
        public Vector2 GetForceAtPosition(Vector2 pos)
        {
            //Create a vector to track our force. Starts at 0.
            Vector2 force = new Vector2(0, 0);

            //The distance between them.
            Vector2 distance = pos - position;
            float dist = (float) Math.Sqrt(distance.X*distance.X + distance.Y*distance.Y);

            //Calculate the normal of the directional force applied.
            Vector2 normal = new Vector2(distance.X, distance.Y);
            normal.Normalize();

            //If we're within the cone, return applied force.
            if (PointInsideCone(pos, position, rotation - (float)Math.PI / 2.0f) ||
                (type == MagnetType.Polar && PointInsideCone(pos, position, rotation + (float)Math.PI / 2.0f)))
            {
                //force.X = attraction * normal.X;
                //force.Y = attraction * normal.Y;
                float totalForce = attraction / (dist * dist);
                force.X = totalForce * (distance.X / dist);
                force.Y = totalForce * (distance.Y / dist);
            }

            //force.X /= distance.X * distance.X;
            //force.Y /= distance.Y * distance.Y;

            return force;
        }

        public MagnetType GetMagnetType()
        {
            return type;
        }

        public bool CanBeToggled()
        {
            return canBeToggled;
        }

        protected override void Move(float delta)
        {
            if (this is Player)
                base.Move(delta);
        }

        // Check if a point lies inside a conical region.
        // Pass the point and the rotation to use.
        private bool PointInsideCone(Vector2 point,Vector2 myPoint, float rot)
        {
            Vector2 vec = Vector2.Normalize(point - myPoint);
            Vector2 forward = new Vector2((float)Math.Cos(rot),(float)Math.Sin(rot));
            forward.Normalize();
            return Vector2.Dot(forward, vec) >= Math.Cos(theta);
        }
    }
}
